import React, {
  Component,
  createRef,
  createContext,
  useRef,
  useState,
  useEffect,
} from "react";
import PropType from "prop-types";
import "./Home.css";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap"; //动画库
import Stats from "stats.js"; //性能监控
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js"; // FBX 模型加载器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; //模型加载器
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; //模型解压缩

import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader"; //模型加载器
// 1、小汽车优质模型使用
// 2、解决 typesScript 抱怨的问题。 通过 // @ts-ignore 和 as 断言

const scene = new THREE.Scene();

const renderer = new THREE.WebGLRenderer({ antialias: true });

const camera = new THREE.PerspectiveCamera(
  45,
  window.innerWidth / window.innerHeight,
  0.25,
  20
);

camera.position.set(4.25, 1.4, -4.5);
const dracoLoader = new DRACOLoader();

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 阻尼开启
controls.maxDistance = 9; //你能够将相机向外移动多少（仅适用于PerspectiveCamera），其默认值为Infinity。
 controls.target.set(0, 0.5, 0);
const loader = new GLTFLoader(); //加载器

const bodyMaterial = new THREE.MeshPhysicalMaterial({
  // 车身 物理网格材质 初始值
  color: 0xff0000, //属性color例外，其可以作为十六进制字符串传递，默认情况下为 0xffffff（白色），内部调用Color.set(color)。
  metalness: 1.0, //材质与金属的相似度。非金属材质，如木材或石材，使用0.0，金属使用1.0，通常没有中间值。 默认值为0.0。0.0到1.0之间的值可用于生锈金属的外观。如果还提供了metalnessMap，则两个值相乘。
  roughness: 0.5, //材质的粗糙程度。0.0表示平滑的镜面反射，1.0表示完全漫反射。默认值为1.0。如果还提供roughnessMap，则两个值相乘。
  clearcoat: 1.0, //表示clear coat层的强度，范围从0.0到1.0m，当需要在表面加一层薄薄的半透明材质的时候，可以使用与clear coat相关的属性，默认为0.0;
  clearcoatRoughness: 0.03, //clear coat层的粗糙度，由0.0到1.0。 默认为0.0
  sheen: 0.5, //光泽层的强度,范围是0.0到1.0。默认为0.0。
});

const detailsMaterial = new THREE.MeshStandardMaterial({
  // 车内 细节
  color: 0xffffff,
  metalness: 1.0,
  roughness: 0.5,
});

const glassMaterial = new THREE.MeshPhysicalMaterial({
  // 车的玻璃
  color: 0xffffff,
  metalness: 0.25,
  roughness: 0,
  transmission: 1.0, //透光率（或者说透光性），范围从0.0到1.0。默认值是0.0。很薄的透明或者半透明的塑料、玻璃材质即便在几乎完全透明的情况下仍旧会保留反射的光线，透光性属性用于这种类型的材质。当透光率不为0的时候, opacity透明度应设置为1.
});


let grid:any = new THREE.GridHelper(20, 40, 0xffffff, 0xffffff); //坐标格辅助对象. 坐标格实际上是2维线数组.
grid.material.opacity = 0.2;
grid.material.depthWrite = true; //渲染此材质是否对深度缓冲区有任何影响。默认为true。(一旦设置transparent为true,这个就失效了，它默认是true，看它的作用就知道它性能开销很大，所以关掉它 )
grid.material.transparent = true; //定义此材质是否透明。这对渲染有影响，因为透明对象需要特殊处理，并在非透明对象之后渲染。设置为true时，通过设置材质的opacity属性来控制材质透明的程度。 默认值为false。
scene.add(grid);

scene.background = new THREE.Color(0x333333); // 场景颜色
scene.environment = new RGBELoader().load("./venice_sunset_1k.hdr"); // 若该值不为null，则该纹理贴图将会被设为场景中所有物理材质的环境贴图。 然而，该属性不能够覆盖已存在的、已分配给 MeshStandardMaterial.envMap 的贴图。默认为null。
scene.environment.mapping = THREE.EquirectangularReflectionMapping; //用于等距圆柱投影的环境贴图，也被叫做经纬线映射贴图 （环境中的灯光也会映射）
scene.fog = new THREE.Fog(0x333333, 10, 15); //雾
dracoLoader.setDecoderPath("../draco/"); //压缩的几何体加载程序【这个要注意，这个是threeJS 的坑，这个压缩的glb 模型，需要这个加载，否则报错。而这个玩意是node_modules three 里面，这个又拿不到，所以把它复制到public中】
loader.setDRACOLoader(dracoLoader); //用于解码使用KHR_draco_mesh_compression扩展压缩过的文件。
const ReactDemo = () => {
 
  const wheels:Array<THREE.Object3D | undefined> = []
  const [bodyValue, setBodyValue] = useState<string>("#ff0000");
  const [detailsValue, setDetailsValue] = useState<string>("#ffffff");
  const [glassValue, setGlassValue] = useState<string>("#ffffff");
  const handleChange = (e: any, type: string) => {
    switch (type) {
      case "body":
        setBodyValue(e.target.value);
        bodyMaterial.color.set(bodyValue);
        break;

      case "details":
        setDetailsValue(e.target.value);
        detailsMaterial.color.set(detailsValue);
        break;

      case "glass":
        setGlassValue(e.target.value);
        glassMaterial.color.set(glassValue);
        break;
    }
  };

  useEffect(() => {
    init();
    animate();
   
  }, []);
  function init() {
    const container = document.createElement("div");
    document.body.appendChild(container);
    loader.load("./3D/ferrari.glb", function (gltf) {
      const carModel = gltf.scene.children[0];

      (carModel.getObjectByName("body") as THREE.Mesh).material = bodyMaterial; // 车身 材质覆盖
      //@ts-ignore
      carModel.getObjectByName("rim_fl").material = detailsMaterial; //轮子骨架 材质覆盖
      //@ts-ignore
      carModel.getObjectByName("rim_fr").material = detailsMaterial;//轮子骨架 材质覆盖
      (carModel.getObjectByName("rim_rr") as THREE.Mesh).material =detailsMaterial;//轮子骨架 材质覆盖
      (carModel.getObjectByName("rim_rl") as THREE.Mesh).material =detailsMaterial; //轮子骨架 材质覆盖
      //@ts-ignore
      carModel.getObjectByName("trim").material = detailsMaterial; //车子内饰 材质覆盖
      //@ts-ignore
      carModel.getObjectByName( 'glass' ).material = glassMaterial; //车子玻璃  材质覆盖

      wheels.push( //获取4个轮子
        carModel.getObjectByName( 'wheel_fl' ),
				carModel.getObjectByName( 'wheel_fr' ),
				carModel.getObjectByName( 'wheel_rl' ),
				carModel.getObjectByName( 'wheel_rr' )
      )

      scene.add(carModel);
    });

    renderer.setPixelRatio(window.devicePixelRatio); //设置设备像素比。通常用于避免HiDPI设备上绘图模糊
    renderer.setSize(window.innerWidth, window.innerHeight); // canvas 大小设置
    renderer.toneMapping = THREE.ACESFilmicToneMapping; //这些常量定义了WebGLRenderer中toneMapping的属性。 这个属性用于在普通计算机显示器或者移动设备屏幕等低动态范围介质上，模拟、逼近高动态范围（HDR）效果。
    renderer.toneMappingExposure = 0.8; //色调映射的曝光级别。默认是1
    renderer.outputEncoding = THREE.sRGBEncoding; //定义渲染器的输出编码
    container.appendChild(renderer.domElement); // 把canvas 添加到 到div 中

    window.addEventListener("resize", onWindowResize);
  }

  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
  }

  //

  function animate() {
    
    renderer.setAnimationLoop( animate );
  
    // performance.now() 返回从性能测量开始到现在的毫秒数
    const time =  -performance.now() / 1000;
  
    for ( let i = 0; i < wheels.length; i ++ ) {
      wheels[ i ]!.rotation.x = time * Math.PI * 2; // 轮子滚起来
    }
    grid.position.z = -( time ) % 1;  //底部的网格z轴运动
    renderer.render(scene, camera);

    controls.update();
  }
  return (
    <>
      <div className="divBox">
        <span className="colorPicker">
          <input
            id="body-color"
            type="color"
            value={bodyValue}
            onChange={(e) => handleChange(e, "body")}
          ></input>
          <br />
          车身
        </span>
        <span className="colorPicker">
          <input
            id="details-color"
            type="color"
            onChange={(e) => handleChange(e, "details")}
            value={detailsValue}
          ></input>
          <br />
          细节
        </span>
        <span className="colorPicker">
          <input
            id="glass-color"
            type="color"
            onChange={(e) => handleChange(e, "glass")}
            value={glassValue}
          ></input>
          <br />
          玻璃
        </span>
      </div>
    </>
  );
};

export default ReactDemo;
